package top.hekun.study.tree;

import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.tree.*;
import top.hekun.study.core.util.ClassFileUtil;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import static org.objectweb.asm.Opcodes.*;


/**
 * 以树结构 生成类文件
 */
public class TreeGenerationClass {
    public static void main(String[] args) {
        // 获取类文件存放位置
        File classFile = getClassFile();
        ClassNode classNode = new ClassNode();
        // 生成类信息
        generateClassInfo(classNode);
        // 生成字段信息
        generateClassFieldInfo(classNode);
        // 设置方法信息
        generateClassMethodInfo(classNode);
        // 设置方法并 写代码
        generateClassMethodInfoAndCode(classNode);
        byte[] classFileCore = getClassFileCore(classNode);
        writeClassFileCore(classFileCore, classFile);
    }

    /**
     * 生成类信息
     *
     * @param classNode
     */
    private static void generateClassInfo(ClassNode classNode) {
        // 设置类的版本
        classNode.version = V1_8;
        // 类的描述符
        classNode.access = ACC_PUBLIC;
        // 类的全路径
        classNode.name = "top/hekun/study/classFileModel/TreeGenerationHelloWord";
        // 父类
        classNode.superName = "java/lang/Object";
        // 接口
        List<String> interfaces = classNode.interfaces;
        interfaces.add("a/b/c/d0");
        interfaces.add("a/b/c/d1");

    }

    private static void generateClassFieldInfo(ClassNode classNode) {
        // 创建一个 int 类型的 age 字段
        classNode.fields.add(new FieldNode(ACC_PUBLIC | ACC_FINAL | ACC_STATIC, "age", "I", null, 22));
        // 创建一个 String 类型的 name 字段
        FieldNode fieldNode = new FieldNode(ACC_PUBLIC | ACC_FINAL | ACC_STATIC, "name", "Ljava/lang/String;", null, "坤坤");
        fieldNode.visibleAnnotations = new ArrayList<>();
        // 给字段设置注解
        AnnotationNode annotationNode = new AnnotationNode("Ltop/hekun/study/annotation/MyAnnotation;");
        annotationNode.visit("name", "坤坤的注解");
        annotationNode.visit("age", new Integer(1));
        fieldNode.visibleAnnotations.add(annotationNode);
        classNode.fields.add(fieldNode);

    }

    /**
     * 生成方好
     *
     * @param classNode
     */
    private static void generateClassMethodInfo(ClassNode classNode) {
        classNode.methods.add(new MethodNode(ACC_PUBLIC | ACC_ABSTRACT, "setName", "(Ljava/lang/String;IJ)I", null, null));
    }


    /**
     * 生成方法和代码
     *
     * @param classNode
     */
    private static void generateClassMethodInfoAndCode(ClassNode classNode) {
        MethodNode methodNode = new MethodNode(ACC_PUBLIC, "printlnTxt", "()V", null, null);
        // 代码顺序
        InsnList insnList = methodNode.instructions;
        // 同写 core d代码一样
        insnList.add(new FieldInsnNode(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"));
        insnList.add(new LdcInsnNode("动态插入方法 printlnTxt"));
        insnList.add(new MethodInsnNode(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/io/String;)V", false));
        insnList.add(new InsnNode(RETURN));
        classNode.methods.add(methodNode);

    }

    /**
     * 生成字节码
     *
     * @param classNode
     * @return
     */
    private static byte[] getClassFileCore(ClassNode classNode) {
        // 获取类生成器
        ClassWriter classWriter = getClassWriter();
        classNode.accept(classWriter);
        return classWriter.toByteArray();
    }

    /**
     * 获取类文件
     *
     * @return
     */
    private static File getClassFile() {
        String className = "TreeGenerationHelloWord";
        File file = new File(ClassFileUtil.getClassFileModelPath(), className + ".class");
        return file;
    }

    /**
     * 获取 ClassWriter 对象
     *
     * @return
     */
    private static ClassWriter getClassWriter() {
        ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
        return classWriter;
    }

    /**
     * 文件写入数据
     *
     * @param core
     * @param classFilePath
     */
    private static void writeClassFileCore(byte[] core, File classFilePath) {
        ClassFileUtil.classFileCoreWrite(core, classFilePath);
    }
}
